﻿using System;
using System.Collections.Generic;
using System.Linq;
using Xenta.Api;
using Xenta.Entities;
using Xenta.Enums;
using Xenta.Plugins;
using Xenta.Security;
using Xenta.Utils;

namespace Xenta.Operations
{
    #region __General__

    /// <summary>
    /// Creates a currency.
    /// </summary>
    public class CreateCurrency : CreateAuditable<CurrencyEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("GLBLZ");
            }
        }

        /// <summary>
        /// Gets or sets the code.
        /// </summary>
        public string Code
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the name.
        /// </summary>
        public string Name
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the currency rate.
        /// </summary>
        public decimal Rate
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the price format.
        /// </summary>
        public string PriceFormat
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the flags.
        /// </summary>
        public CurrencyFlags Flags
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override Err Validate(IUnitOfWork uow)
        {
            if(String.IsNullOrWhiteSpace(Code))
                return Err.Of<CurrencyEntity>().NotValid("Code");
            if(Rate <= Decimal.Zero)
                return Err.Of<CurrencyEntity>().NotValid("Rate");
            return base.Validate(uow);
        }

        protected override void Construct(IUnitOfWork uow, CurrencyEntity entity)
        {
            base.Construct(uow, entity);

            entity.Code = Code.TrimToLen(3);
            entity.Name = StringHelper.EnsureNotNull(Name).TrimToLen(100);
            entity.Rate = Rate;
            entity.PriceFormat = StringHelper.EnsureNotNull(PriceFormat).TrimToLen(100);
            entity.Flags = Flags;
        }

        #endregion
    }

    /// <summary>
    /// Updates the currency.
    /// </summary>
    public class UpdateCurrency : UpdateAuditable<CurrencyEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("GLBLZ");
            }
        }

        /// <summary>
        /// Gets or sets the code.
        /// </summary>
        public string Code
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the name.
        /// </summary>
        public string Name
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the currency rate.
        /// </summary>
        public decimal Rate
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the price format.
        /// </summary>
        public string PriceFormat
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the flags.
        /// </summary>
        public CurrencyFlags Flags
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override Err Validate(IUnitOfWork uow)
        {
            if(String.IsNullOrWhiteSpace(Code))
                return Err.Of<CurrencyEntity>().NotValid("Code");
            if(Rate <= Decimal.Zero)
                return Err.Of<CurrencyEntity>().NotValid("Rate");
            return base.Validate(uow);
        }

        protected override void Revise(IUnitOfWork uow, CurrencyEntity entity)
        {
            base.Revise(uow, entity);

            entity.Code = Code.TrimToLen(3);
            entity.Name = StringHelper.EnsureNotNull(Name).TrimToLen(100);
            entity.Rate = Rate;
            entity.PriceFormat = StringHelper.EnsureNotNull(PriceFormat).TrimToLen(100);
            entity.Flags = Flags;
        }

        #endregion
    }

    /// <summary>
    /// Deletes the currency.
    /// </summary>
    public class DeleteCurrency : DeleteEntity<CurrencyEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("GLBLZ");
            }
        }

        #endregion
    }

    /// <summary>
    /// Gets the currency by identifier.
    /// </summary>
    public class GetCurrency : GetEntity<CurrencyEntity>,
        IApiOperation
    {
    }

    /// <summary>
    /// Gets the currency by code.
    /// </summary>
    public class GetCurrencyByCode : QueryEntity<CurrencyEntity>,
        IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets or sets the country code.
        /// </summary>
        public string Code
        {
            get;
            set;
        }

        #endregion

        #region Options

        protected override bool Cacheable
        {
            get
            {
                return true;
            }
        }

        #endregion

        #region Utilities

        protected override IQueryable<CurrencyEntity> Build(IUnitOfWork uow, IQueryable<CurrencyEntity> query)
        {
            string code = StringHelper.EnsureNotNull(Code).TrimToLen(3);
            return base.Build(uow, query).Where(x => x.Code.Equals(code));
        }

        #endregion
    }

    #endregion

    #region __Search__

    /// <summary>
    /// Search over currency collection.
    /// </summary>
    public class SearchCurrencies : SearchOperation<CurrencyEntity>,
        IApiOperation
    {
        #region Ctors

        /// <summary>
        /// Initializes a new class instance.
        /// </summary>
        public SearchCurrencies()
        {
            SortDir = SortDirection.Ascending;
            SortBy = new []
            {
                CurrencySortBy.ID
            };
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the flag mask.
        /// </summary>
        public CurrencyFlags FlagMask
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the sort direction.
        /// </summary>
        public SortDirection SortDir
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the sort by.
        /// </summary>
        public CurrencySortBy[] SortBy
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override IQueryable<CurrencyEntity> Build(IUnitOfWork uow, IQueryable<CurrencyEntity> query)
        {
            query = base.Build(uow, query);
            query = query.FilterByFlags(x => (int)x.Flags, (int)FlagMask);
            if(SortBy != null)
            {
                for(int i = 0; i < SortBy.Length; i++)
                {
                    switch(SortBy[i])
                    {
                        case CurrencySortBy.Code:
                            query = query.OrderBy(x => x.Code, SortDir, i != 0);
                            break;
                        case CurrencySortBy.Name:
                            query = query.OrderBy(x => x.Name, SortDir, i != 0);
                            break;
                        case CurrencySortBy.Rate:
                            query = query.OrderBy(x => x.Rate, SortDir, i != 0);
                            break;
                        default:
                            query = query.OrderBy(x => x.EntityID, SortDir, i != 0);
                            break;
                    }
                }
            }
            return query;
        }

        #endregion
    }

    /// <summary>
    /// Gets the collection of currencies as a dictionary, where 
    /// key - currency identifier and value - currency code.
    /// </summary>
    public class GetCurrencyDictionary : SearchOperation<CurrencyEntity, Int32, String>,
        IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets or sets the flag mask.
        /// </summary>
        public CurrencyFlags FlagMask
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override IQueryable<CurrencyEntity> Build(IUnitOfWork uow, IQueryable<CurrencyEntity> query)
        {
            return base.Build(uow, query)
                .FilterByFlags(x => (int)x.Flags, (int)FlagMask)
                .OrderBy(x => x.Code);
        }

        protected override IQueryable<KeyValuePair<Int32, String>> Map(IQueryable<CurrencyEntity> query)
        {
            return query.Select(x => new KeyValuePair<Int32, String>(x.EntityID, x.Name));
        }

        #endregion
    }

    #endregion

    #region __Bulk__

    /// <summary>
    /// Updates the currency exchange rates from a dictionary.
    /// </summary>
    public class BulkUpdateCurrencyRates : Operation,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("GLBLZ");
            }
        }

        /// <summary>
        /// Gets or sets the exchange rate dictionary, where key 
        /// is a currency code and value is an exchange rate.
        /// </summary>
        public IDictionary<String, Decimal> Rates
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override Err Validate(IUnitOfWork uow)
        {
            if(Rates == null || !Rates.Any())
                return Err.Of(this).No("Rates");
            return base.Validate(uow);
        }

        protected override void Work(IUnitOfWork uow)
        {
            foreach(var i in Rates)
            {
                var currency = uow.Execute(new GetCurrencyByCode
                {
                    Code = i.Key
                }).Result;
                if(currency == null || currency.Rate == i.Value)
                    continue;
                uow.Execute(new UpdateCurrency
                {
                    EntityID = currency.EntityID,
                    Code = currency.Code,
                    Name = currency.Name,
                    PriceFormat = currency.PriceFormat,
                    Rate = i.Value,
                    Flags = currency.Flags
                });
            }
        }

        #endregion
    }

    /// <summary>
    /// Updates the currency exchange using specified provider.
    /// </summary>
    public class BulkUpdateCurrencyRatesUsingProvider : Operation,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("GLBLZ");
            }
        }

        /// <summary>
        /// Gets or sets the currency rate provider.
        /// </summary>
        public Guid ProviderID
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override Err Validate(IUnitOfWork uow)
        {
            if(!uow.Infrastructure.CheckDependency<ICurrencyRateProvider>(ProviderID))
                return Err.Of<ICurrencyRateProvider>().NotFound();
            return base.Validate(uow);
        }

        protected override void Work(IUnitOfWork uow)
        {
            var rates = uow.Execute(new SearchCurrencies())
                .Result.ToDictionary(x => x, x => x.Rate);
            using(var ioc = uow.Infrastructure.DependencyResolver())
                (ioc.Resolve(ProviderID) as ICurrencyRateProvider).GetRates(rates);
            uow.Execute(new BulkUpdateCurrencyRates
            {
                Rates = rates.ToDictionary(x => x.Key.Code, x => x.Value)
            });
        }

        #endregion
    }

    #endregion
}
